home *** CD-ROM | disk | FTP | other *** search
/ Programmer Power Tools / Programmer Power Tools.iso / microcrn / issue_39.arc / REAL39.FIG < prev    next >
Encoding:
Text File  |  1987-12-01  |  18.4 KB  |  409 lines

  1. /*** Complete listing (1) -- CLOCKINT.C, MYFUNC.C, SERIAL.C, COLORS.H ***/
  2.  
  3.  
  4. /* CLOCKINT.C : Turbo C program to install your own interrupt
  5. function in the clock interrupt, while still preserving everyone
  6. else's. Bruce Eckel, Eisys Consulting 1987. To make the control
  7. program, you must create a project file which contains the
  8. following, and make it's file the "project name": colors.h is a
  9. header which the C file includes; if the header gets changed,
  10. that C file will be re-compiled even if it wasn't modified
  11. itself. */
  12.  
  13. #define CLOCKINT 8  /* IRQ0 on the 8259 is interrupt 8 in the
  14. vector table */
  15.  
  16. #define PROG_SIZE 0x87F  /* Run the Turbo C compiler with the
  17.   options:linker:mapfile set to "segments."  Look at the mapfile
  18.   generated for this program.  The "stop" address for the stack is
  19.   the highest adress used -- set PROG_SIZE to this value for use
  20.   with the "keep()" command */
  21.  
  22. void interrupt (*old_int_handler)();
  23.  
  24. /* This, believe it or not, is a variable declaration.  I've put
  25.   it way out here (outside of any function declarations) so it
  26.   doesn't go away when main exits (i.e. it's global). It holds the
  27.   address of the old interrupt handler function so the NEW
  28.   interrupt handler can call the OLD one when it's through. To read
  29.   it, start in the middle and work your way out: old_int_handler is
  30.   a POINTER ("*") to a FUNCTION ("()") of type "interrupt"; this
  31.   function doesn't return a value ("void").  */
  32.  
  33. main()
  34.         /* all this does is install the interrupt handler.  I
  35.         don't use any "printf()s" because it increases the 
  36.         resident program size so much. */ 
  37. {
  38.  
  39. /* first: function prototypes, right out of the book.  These 
  40.   don't generate any code, but they tell the compiler (and us) 
  41.   exactly how the functions should be called so it can catch 
  42.   mistakes.  The same effect can be achieved with "#include 
  43.   <dos.h>", but this is more educational. */ 
  44.  
  45.  extern void interrupt (*getvect(int intr_num))();
  46. /* The above declaration is tricky to read: getvect returns a 
  47.   pointer to a function of type interrupt (and an "interrupt" 
  48.   function never returns a value, thus the "void").  It's deceiving 
  49.   because the function "getvect" is actually returning something, 
  50.   but the first word you see is "void" */ 
  51.  
  52.  extern void setvect(int intr_num, void interrupt (*isr)());
  53. /* This function really DOESN'T return return anything.  It must 
  54.   be passed an integer and a pointer to a function of type
  55.   interrupt which has no return value.  The ability to pass 
  56.   function addresses around allows us to almost entirely eliminate 
  57.   the need for assembly language when using C. */
  58.  
  59.  void interrupt my_int_handler();
  60.  /* Prototype for the interrupt we are inserting in the clock 
  61.    chain (a lot like inserting an item in a linked list).  Notice 
  62.    the word "extern" doesn't appear in this declaration because the 
  63.    function definition is contained in this file. */ 
  64.  
  65. /* The code is trivial: get the old clock interrupt vector (for 
  66.   our routine to pass control to when it's done), install our 
  67.   routine as the new interrupt service routine, and call "terminate 
  68.   but stay ready." */ 
  69.  
  70.  old_int_handler = getvect(CLOCKINT); /* store the address of the old 
  71.                                       handler */
  72.  setvect(CLOCKINT, my_int_handler);
  73.  keep(0,PROG_SIZE);               /* first parameter is exit status */
  74.  }
  75.  
  76. void interrupt my_int_handler()
  77. {
  78. extern void my_function();  /* i.e.: this is defined in another file */
  79. const int clock_count = 9; /* wait this many ints between invoking our
  80.                  function; there are about 18 interrupts per second. */
  81. static int i = 0;          /* The interrupt counter doesn't go away 
  82.                      between interrupts */
  83.  
  84. if(i++ == clock_count) { my_function(); i = 0;}  /* Do whatever we want... */
  85.   /* (with the usual caveat that we can't make any DOS or BIOS calls) */
  86. (*old_int_handler)();  /* ... and continue the chain by de-referencing the
  87.                           pointer to the old clock service routine.  */
  88. /*  This was just TOO easy !! */
  89. }
  90.  
  91. /* A caveat: I haven't done extensive testing, so this could slow 
  92.   down the time-of-day clock on the PC.  If it does, one might add 
  93.   a function to restore the correct time from the battery-backed 
  94.   clock-calendar.  */ 
  95.  
  96.  
  97.  
  98.  
  99.  
  100. /* MYFUNC.C: Turbo C function installed as a TSR by CLOCKINT.C.
  101.   Bruce Eckel, Eisys Consulting 1987.  This integrates the serial
  102.   input and output functions, and some screen display code from
  103.   issue #38 (which must be used since an interrupt routine can't
  104.   make DOS calls; i.e. DOS isn't re-entrant).  Each time
  105.   my_function() is called, it outputs a new character from the
  106.   led_table[] to the serial-to-parallel chip, gets a byte from the
  107.   parallel-to-serial chip, and displays the byte as ones and zeroes 
  108.   in the lower right corner of the screen. 
  109.  
  110.     You can replace the code in my_function() with anything else 
  111.   (as long as your code or any C library functions you call don't 
  112.   make DOS or ROM BIOS calls -- printf() does) to create your own 
  113.   clock interrupt background process. */
  114.  
  115. #include "colors.h"  /* #defines for the CGA */
  116.  
  117. /* Now we tell the compiler that "led_table[]" is in another 
  118.   file.  We still have to show what it looks like so the compiler 
  119.   can generate the right code when we reference elements of the 
  120.   structure.  The actual address of led_table[] is figured out 
  121.   ("resolved") by the linker.  This type of declaration is often
  122.   contained in a header file as a "typedef."  The header file is 
  123.   then included in the file where the table is defined and any 
  124.   other files where it is declared.  Note, however, that a header 
  125.   file with anything other than definitions in it (i.e. code) is 
  126.   asking for trouble. */ 
  127.  
  128. extern const struct {
  129.        char character;
  130.        unsigned char code;
  131.        } led_table[];
  132.  
  133. /* Here's the function which is called by my_int_handler() */
  134. void my_function()
  135. {
  136. void display_byte(unsigned char byte);
  137. extern void outbyte(unsigned char ch);
  138. extern unsigned char inbyte();
  139.  
  140. static int i = 0;  /* statics don't go away between function 
  141.      calls, so we don't output the first character every time.  It's 
  142.      only initialized to zero when the program is loaded. */ 
  143.  
  144. display_byte(inbyte());  /* Read the LS165 and display it on the screen. */
  145. outbyte(led_table[i].code); /* send a character to the seven-segment 
  146.                                display.*/
  147. if(led_table[++i].code == 0) /* increment the table index and see if 
  148.                              we're at the end of the table; */
  149.    i = 0;                    /* if so, start over.      */
  150. }
  151.  
  152. /* This function displays a byte as ones and zeroes in the lower 
  153.   right corner of the CGA screen, without calling printf(). */
  154.  
  155. void display_byte(unsigned char byte)
  156. {
  157. void putc_at_location(char, int, int, unsigned char);
  158.  
  159. /* you don't HAVE to give variable names in a declaration, but it's not
  160.   as clear if you don't. */
  161.  
  162.  int i, column = 64;  /* "line" and "column" are the starting locations */
  163.  const int line = 24; /* The compiler barfs if we try to change a constant.*/
  164.  for( i = 7; i >= 0; i--){ /* start with the high bit and work down */
  165.       putc_at_location(byte & (1 << i) ? '1' : '0', column++, line,
  166.                        LIGHT_GREEN_CHAR | RED_BACK);
  167.       /* See SERIAL.C for a description of the "?:" ternary expression. */
  168.       putc_at_location(' ', column++, line, BLACK_CHAR | RED_BACK);
  169.       }
  170. }
  171.  
  172. /* Puts a character and its attribute anywhere on the screen. */
  173. void putc_at_location(char ch, int x, int y, unsigned char attribute)
  174. {
  175. pokeb(SCREEN_BASE,((y * SCREEN_WIDTH) + x) * 2,ch);
  176. pokeb (SCREEN_BASE,(((y * SCREEN_WIDTH) + x) * 2) + 1, attribute);
  177. }
  178.  
  179.  
  180.  
  181.  
  182.  
  183. /* SERIAL.C: Turbo C program to write to a TTL LS164 and read
  184.   from an LS165 through a parallel printer card.  Bruce Eckel,
  185.   Eisys Consulting 1987. You can create either a stand-alone
  186.   program for testing, or link the compiled file into the
  187.   "CLOCKINT.C" project using CLOCKINT.PRJ. */
  188.  
  189. #undef test  /* #define test creates a stand-alone test program
  190.               from this file */
  191. #define BASE 0x238 /* I modified my $21 Microsphere card so it's in an
  192.                    unused address space (no collisions with other
  193.                    printer cards) */
  194.  
  195. #define BIT(I) (unsigned char)(1 << I)  /* this should be evaluated 
  196.                    at compile time so there won't be any overhead at 
  197.                    run time.  The "(unsigned char)" is called a "cast" 
  198.                    (like casting into a mold); it forces the result to 
  199.                    be that data type (in this case, one byte long and 
  200.                    don't mess with the high bit). */
  201.  
  202. /* These definitions make the code much easier to follow (and 
  203.   thus less prone to error). "ORing" with a bit sets that bit to 
  204.   "1", while "ANDing" with the complement of a bit sets that bit to 
  205.   zero.  I'm being sort of tricky here: BASE is the printer data
  206.   address and can be read from as well as written to, so I read it, 
  207.   change a single bit, and write it out again so the other bits 
  208.   aren't changed and can be used for other applications.  You can't 
  209.   do this with the other printer port locations (BASE+1 and 
  210.   BASE+2).  It would probably be more proper to use global memory 
  211.   locations to preserve the state of all three. */ 
  212.  
  213. /* The output clock for the LS164:  */
  214. #define LOWER_CLOCK_O   outport(BASE,inport(BASE) & ~BIT(0)) /* pin 2 
  215.                                                              on DB-25 */
  216. #define RAISE_CLOCK_O   outport(BASE,inport(BASE) |  BIT(0))
  217.                        /* Changing the output data bit for the LS164: */
  218. #define DATA_0 (inport(BASE) & ~BIT(1))                      /* pin 3 on 
  219.                                                              DB-25 */
  220. #define DATA_1 (inport(BASE) |  BIT(1))
  221.                        /* Load and shift control for the LS165: */
  222. #define LOAD_LS165    outport(BASE, inport(BASE) & ~BIT(2)) /* pin 4 on 
  223.                                                              DB-25 */
  224. #define SHIFT_LS165   outport(BASE, inport(BASE) | BIT(2))
  225.                        /* The input clock for the LS165:  */
  226. #define LOWER_CLOCK_I   outport(BASE,inport(BASE) & ~BIT(3)) /* pin 5 on 
  227.                                                              DB-25 */
  228. #define RAISE_CLOCK_I   outport(BASE,inport(BASE) |  BIT(3))
  229.  
  230. /* This is a table with characters and the codes which generate them on
  231.    the seven-segment display: */
  232. const struct {              /* Notice the array declaration led_table[]. */
  233.        char character;      /* You don't have to put the number of array */
  234.        unsigned char code;  /* entries in the brackets; the compiler     */
  235.        } led_table[] = {    /* counts them for you!                      */
  236.          { '0', 0x3f},      /* The seven-segment display:                */
  237.          { '1', 0x30},      /*                                           */
  238.          { '2', 0x5b},      /*           a: bit 0                         */
  239.          { '3', 0x4f},  /*               ________                         */
  240.          { '4', 0x66},  /*              |        |                        */
  241.          { '5', 0x6d},  /*   f: bit 5   |        | b: bit 1               */
  242.          { '6', 0x7d},  /*              |   g:   |                        */
  243.          { '7', 0x07},  /*              |________|                        */
  244.          { '8', 0x7f},  /*              | bit 6  |                        */
  245.          { '9', 0x6f},  /*   e: bit 4   |        | c: bit 2               */
  246.          { 'A', 0x77},  /*              |        |                        */
  247.          { 'B', 0x7c},  /*              |________|                        */
  248.          { 'C', 0x39},  /*                d: bit 3                        */
  249.          { 'D', 0x5e},  /*                           [*]                  */
  250.          { 'E', 0x79},  /*                           h: bit 7             */
  251.          { 'F', 0x71},  /*                                                */
  252.          { 'H', 0x76},  /*  For example, the number 5 would use segments: */
  253.          { 'J', 0x1e},  /*  a, c, d, f & g which forms the binary number: */
  254.          { 'L', 0x38},  /*  01101101 or the hex number 0x6d               */
  255.          { 'N', 0x54},  /*                                                */
  256.          { 'O', 0x3f},  /*  Some of the letters require a stretch of the  */
  257.          { 'P', 0x73},  /*  imagination, but they get the message across. */
  258.          { 'R', 0x50},  /*                                                */
  259.          { 'S', 0x6d},
  260.          { 'T', 0x78},
  261.          { 'U', 0x3E},
  262.          { 'Y', 0x6E},
  263.          { '?', 0x53},
  264.          { '.', 0x80},
  265.          { '-', 0x40},
  266.          { '=', 0x48},
  267.          { '_', 0x08},
  268.          { '\0',0} /* this tags the end of the table, so new entries can be
  269.                       added without any other code modification. */
  270.      };
  271.  
  272. #if defined(test)   /* for stand-alone testing */
  273. main()
  274. {  /* function declarations first: */
  275.  void outbyte(unsigned char ch);
  276.  unsigned char inbyte();
  277.  void print_byte(unsigned char byte);
  278.  int i = 0;
  279.  char ch;
  280.  do {
  281.     print_byte(inbyte());  /* get a byte from the parallel-to-serial chip
  282.                               and display it in binary */
  283.     ch = toupper(getch()); /* get a keyboard character and convert to 
  284.                               upper case */
  285.     /* search the table until you find the character or hit the end. */
  286.     for(i = 0; led_table[i].character != ch && led_table[i].code != 0; i++)
  287.           ; /* All the work is done inside the "for" parentheses. */
  288.     outbyte(led_table[i].code);  /* send the code to the 
  289.                                  serial-to-parallel chip */
  290.   } while ( ch != 27 );  /* repeat until the "escape" key is pressed */
  291. } /* end of main */
  292.  
  293. /* I'm putting this definition inside the "#if (defined)" conditional
  294.    compilation because the code for printf() is very big and I don't want
  295.    it included when the Terminate-And-Stay-Ready program is created. */
  296.  
  297. void print_byte(unsigned char byte)
  298. {
  299.  int i;
  300.  for(i = 7; i >=0; i--)
  301.        byte & (1 << i) ? printf("1 ") : printf("0 ");
  302.  /* See below for description of the "?:" ternary expression. */
  303.  printf("\n");
  304.  }
  305.  
  306. #endif defined(test)  /* You can put things after an "#endif" as notes to
  307.                          yourself; the compiler ignores them. */
  308.  
  309. /* And now, the function definitions: */
  310. void outbyte(unsigned char byte)
  311. /*  Send a byte to the serial-to-parallel chip (LS164) */
  312. {
  313.  int i;
  314.  RAISE_CLOCK_O;  /* This is just for the benefit of the initial call. */
  315.  for(i = 7; i >= 0; i--){ /* I start with 7 because that bit goes out first */
  316.     LOWER_CLOCK_O;
  317.                           /* Now present the data at pin 1 of the DB-25 */
  318.     outport(BASE, byte & (1 << i) ? DATA_0 : DATA_1); /* Outputs are 
  319.                                                        inverted */
  320.          /* The "ternary expression" returns the value to the left of the ":"
  321.          of the question is true (i.e. non-zero) and the value to the right 
  322.          of the ":" if the question is false (i.e. zero).  It's very nifty 
  323.          and compact, since it not only performs an "if-then-else," but it 
  324.          also returns a value. */
  325.     
  326.     RAISE_CLOCK_O;  /* clock the data in */
  327.     }
  328.  }
  329.  
  330. unsigned char inbyte()
  331. /* read a value from the parallel-to-serial chip (LS165) */
  332. {
  333.  int i;
  334.  unsigned char result = 0;
  335.  RAISE_CLOCK_I; /* Clock should be high before loading data into the chip */
  336.  LOAD_LS165;  /* Move the data at the inputs into the flip-flops */
  337.  SHIFT_LS165; /* Enable shifting out of the data */
  338.  for(i = 7; i >= 0; i--) { /* bit 7 comes out of the LS165 first */
  339.        /* Bit 7 is present at the output immediately when the chip is loaded,
  340.           so we have to pluck it off (into pin 12 of the DB-25) before we do
  341.           any clocking. */
  342.        result |= ((inport(BASE+1) & BIT(5)) ? 1 : 0) << i;
  343.        /* form result:  The ternary expression returns 1 if bit 5 of i/o
  344.           address BASE+1 is a "1" and 0 if it's a zero.  This value is
  345.           shifted into the appropriate bit position by the "<< i".
  346.           result |= is the same as saying: result = result |, so the bit is
  347.           ORed into the result.  This way, we poke each serial bit into the
  348.           correct place in the parallel result. */
  349.        LOWER_CLOCK_I;
  350.        RAISE_CLOCK_I; /* The next bit appears on the rising edge of
  351.                       the clock. */
  352.    }
  353.  return result;
  354. }
  355.  
  356.  
  357.  
  358.  
  359. /* COLORS.H: definitions for CGA screen characteristics and colors */
  360. #define SCREEN_BASE 0xb800   /* base address of color graphics card (and EGA
  361.                                 in color graphics mode  */
  362. #define SCREEN_HEIGHT 25
  363. #define SCREEN_WIDTH 80
  364. #define SCREEN_CHARS (SCREEN_WIDTH * SCREEN_HEIGHT * 2)
  365.                             /* number of chars and attributes in a screen */
  366.  
  367. #define BIT(I) (unsigned char)(1 << I)  /* A bit mask macro.  The compiler
  368.  evaluates it, so it doesn't cost anything. */
  369.  
  370. /* Make a complete attribute by ORing a CHARacter type with a
  371.                             BACKground type */
  372. #define BLUE_CHAR  BIT(0)
  373. #define GREEN_CHAR BIT(1)
  374. #define RED_CHAR   BIT(2)
  375. #define INTENSE    BIT(3)
  376. #define BLUE_BACK  BIT(4)
  377. #define GREEN_BACK BIT(5)
  378. #define RED_BACK   BIT(6)
  379. #define BLINKING   BIT(7)
  380.  
  381. #define BLACK_CHAR 0
  382. #define CYAN_CHAR (GREEN_CHAR | BLUE_CHAR)
  383. #define MAGENTA_CHAR (RED_CHAR | BLUE_CHAR)
  384. #define BROWN_CHAR (RED_CHAR | GREEN_CHAR)
  385. #define WHITE_CHAR (RED_CHAR | GREEN_CHAR | BLUE_CHAR)
  386. #define GRAY_CHAR (INTENSE | BLACK_CHAR)
  387. #define LIGHT_BLUE_CHAR (INTENSE | BLUE_CHAR)
  388. #define LIGHT_GREEN_CHAR (INTENSE | GREEN_CHAR)
  389. #define LIGHT_CYAN_CHAR (INTENSE | CYAN_CHAR)
  390. #define LIGHT_RED_CHAR (INTENSE | RED_CHAR)
  391. #define LIGHT_MAGENTA_CHAR (INTENSE | MAGENTA_CHAR)
  392. #define YELLOW_CHAR (INTENSE | BROWN_CHAR)
  393. #define BRIGHT_WHITE_CHAR ( INTENSE | WHITE_CHAR)
  394.  
  395. #define BLACK_BACK 0
  396. #define CYAN_BACK (GREEN_BACK | BLUE_BACK)
  397. #define MAGENTA_BACK (RED_BACK | BLUE_BACK)
  398. #define BROWN_BACK (RED_BACK | GREEN_BACK)
  399. #define WHITE_BACK (RED_BACK | GREEN_BACK | BLUE_BACK)
  400. #define GRAY_BACK (INTENSE | BLACK_BACK)
  401. #define LIGHT_BLUE_BACK (INTENSE | BLUE_BACK)
  402. #define LIGHT_GREEN_BACK (INTENSE | GREEN_BACK)
  403. #define LIGHT_CYAN_BACK (INTENSE | CYAN_BACK)
  404. #define LIGHT_RED_BACK (INTENSE | RED_CHAR)
  405. #define LIGHT_MAGENTA_BACK (INTENSE | MAGENTA_CHAR)
  406. #define YELLOW_BACK (INTENSE | BROWN_BACK)
  407. #define BRIGHT_WHITE_BACK ( INTENSE | WHITE_BACK)
  408.  
  409.